package spring.spring_aop.aspect;

import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * AOP面向切面编程，是一种编程思想，他的实现方式有spring AOP，AspectJ，CGLIB等
 * AOP是对某一件事的集中处理，是像是拦截器这样，在请求或者返回结果之后对请求或者响应进行处理
 * 不过拦截器是在整个controller生命周期进行处理，而aop能提供到方法的更加精细化的处理
 * 而且对代码是无侵入的 还能达到解耦的目的
 * 在代码中的"execution(* spring.spring_aop.controller.*.*(..))"这路径被称为切点
 * 而满足切点规则的方法被称为连接点
 * 注解类型@Around是通知类型，被修饰的方法中的代码叫做通知，这是每个连接点的共性功能
 * 切面=切点+通知，这样切面就能描述出方法在某些时候需要进行的操作
 * 通知类型有
 * - @Around：在执行前后的操作
 * - @Before：在执行之前的操作
 * - @After：在执行之后的操作
 * - @AfterReturning：在返回结果之后的操作
 * - @AfterThrowing；在抛出异常之后的操作
 * 不过其实只需要一个@Around就都可以完成其他的通知类型的工作
 */
@Slf4j
@Aspect
@Component
public class runTimeAspect {

    //对于相同的切点表达式，可以使用的PointCut来简化
    @Pointcut("execution(* spring.spring_aop.controller.testController.t2(..))")
    public void pt() {}

    //对controller这个目录下的所有类的方法的操作 Around环绕通知，是在方法的前后都执行
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("方法执行之前");

        long startTime = System.currentTimeMillis();
        //具体方法的执行
        Object e = joinPoint.proceed();

        long endTime = System.currentTimeMillis();

        log.info("方法执行后 执行时间： " + (endTime - startTime) + "ms");

        return e;
    }

    @Before("pt()")
    public void before(JoinPoint joinPoint) {
        log.info("方法执行之前" + joinPoint.getSignature().getName());
    }

    //有无异常都执行
    @After("pt()")
    public void after(JoinPoint joinPoint) {
        log.info("方法执行之后" + joinPoint.getSignature().getName());
    }

    //有异常不会执行
    @AfterReturning("pt()")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("结果返回之后" + joinPoint.getSignature().getName());
    }

    //在aop中并不直接操作HttpServletResponse，一般都是在数据层和业务层，要处理的话使用ControllerAdvice拦截器来搞
    @AfterThrowing(pointcut = "execution(* spring.spring_aop.controller.*.*(..))",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Throwable ex) {
        log.info("抛出了异常" + joinPoint.getSignature());
        log.error(ex.toString());
    }

}
